<?php

namespace App\Modules\Payment\Models;

use App\Modules\Payment\IOrder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;

/**
 * Class WeChatRefund
 *
 * @property int $id
 * @property string $status
 * @property string $order_type
 * @property integer $order_id
 * @property bool $notified
 * @property string $out_trade_no
 * @property string $transaction_id
 * @property string $out_refund_no
 * @property int $total_fee
 * @property int $refund_fee
 * @property string $refund_fee_type
 * @property string $refund_desc
 * @property string $refund_account
 * @property string $return_code
 * @property string $return_msg
 * @property string $result_code
 * @property string $err_code
 * @property string $err_code_des
 * @property string $refund_id
 * @property int $settlement_refund_fee
 * @property string $cash_fee_type
 * @property int $cash_refund_fee
 * @property string $refund_recv_accout
 * @property string $success_time
 * @property string $created_at
 * @property string $updated_at
 *
 * @package App\Modules\Order\Models
 */
class WeChatRefund extends Model
{
    protected $fillable = [
        'status',
        'notified',
        'order_type',
        'order_id',
        'out_trade_no',
        'transaction_id',
        'out_refund_no',
        'total_fee',
        'refund_fee',
        'refund_fee_type',
        'refund_desc',
        'refund_account',
        'return_code',
        'return_msg',
        'result_code',
        'err_code',
        'err_code_des',
        'refund_id',
        'settlement_refund_fee',
        'cash_fee_type',
        'cash_refund_fee',
    ];

    protected $casts = [
        'notified' => 'bool',
    ];

    /**
     * Status: initialized
     */
    const STATUS_INITIALIZED = 'INITIALIZE';

    /**
     * Status: processing
     */
    const STATUS_PROCESSING = 'PROCESSING';

    /**
     * Status: success
     */
    const STATUS_SUCCESS = 'SUCCESS';

    /**
     * Status: change
     */
    const STATUS_CHANGE = 'CHANGE';

    /**
     * Status: close
     */
    const STATUS_REFUNDCLOSE = 'REFUNDCLOSE';

    /**
     * Status: fail
     */
    const STATUS_FAIL = 'FAIL';

    /**
     * Status: need retry
     */
    const STATUS_NEED_RETRY = 'NEED_RETRY';

    /**
     * @return bool
     */
    public function execute()
    {
        /** @var \EasyWeChat\Payment\Application $payment */
        $payment = app('wechat.payment.default');

        $result = $payment->refund->byOutTradeNumber(
            $this->out_trade_no,
            $this->out_refund_no,
            $this->total_fee,
            $this->refund_fee,
            [
                'refund_account' => $this->refund_account,
                'notify_url' => config('wechat.payment.default.refund_url'),
            ]
        );

        $this->update(Arr::only($result, [
            'return_code',
            'return_msg',
            'result_code',
            'err_code',
            'err_code_des',
            'refund_id',
            'settlement_refund_fee',
            'cash_fee_type',
            'cash_refund_fee',
        ]));

        if ($this->return_code == 'FAIL') {
            $this->update(['status' => static::STATUS_FAIL]);
            return false;
        }

        if ($this->result_code == 'FAIL') {
            if ($this->err_code == 'SYSTEMERROR' || $this->err_code == 'BIZERR_NEED_RETRY') {
                $this->update(['status' => static::STATUS_NEED_RETRY]);
            } else {
                $this->update(['status' => static::STATUS_FAIL]);
                return false;
            }
        } else {
            $pay = WeChatPay::where('out_trade_no', $this->out_trade_no)->first();
            $pay->refund_fee += $this->refund_fee;
            $pay->save();

            $this->update(['status' => static::STATUS_PROCESSING]);
        }

        return true;
    }

    /**
     * @param array $data
     */
    public function notify(array $data)
    {
        if ($this->notified) {
            return ;
        }
        $this->fill(Arr::only($data, [
            'refund_recv_accout',
            'success_time',
        ]));
        $this->status = $data['refund_status'];
        $this->notified = true;
        $this->save();

        /** @var IOrder $order */
        if($this->status == static::STATUS_CHANGE) {
            $order = ($this->order_type)::query()->where('id', $this->order_id)->first();
            $order->refundFail($this->refund_fee);
        }
    }
}
